#include "FirstPersonHandler.h"
#include <SECore/Events/Input.h>
#include <SECore/Event.h>
#include <SECore/EventDispatcher.h>
#include <SECore/Events/KeyBoardEvent.h>
#include <SEUi/Widget/Widget.h>
#include <glm/gtc/quaternion.hpp>
#include <SEMath/Util/Util.h>


#include <SEDebug/Log/Log.h>

namespace SEngine
{

    FirstPersonHandler::FirstPersonHandler(EnableEventHandle & enHandle, Camera & camera)
        : CameraEventHandler(enHandle, camera)
    {
        m_camera.SetPos(glm::vec3{0.0f});
        m_camera.SetZNear(0.1f);
        m_camera.SetZFar(1000.0f);
        m_targetPos = glm::vec3{30.0f, 10.0f, 30.0f} * 2.0f;
        m_targetFront = -glm::vec3{30.0f, 10.0f, 30.0f} * 2.0f;
    }

    void FirstPersonHandler::OnEvent(EventPtr e)
    {            
        EventDispatcher dispatcher(e);
        dispatcher.Dispatcher<MousePressEvent>(std::bind(&FirstPersonHandler::OnMousePressEvent, this, std::placeholders::_1));
        dispatcher.Dispatcher<MouseReleaseEvent>(std::bind(&FirstPersonHandler::OnMouseReleaseEvent, this, std::placeholders::_1));
        dispatcher.Dispatcher<MouseMoveEvent>(std::bind(&FirstPersonHandler::OnMouseMoveEvent, this, std::placeholders::_1));
        dispatcher.Dispatcher<MouseWheelEvent>(std::bind(&FirstPersonHandler::OnMouseWheelEvent, this, std::placeholders::_1));
        dispatcher.Dispatcher<KeyPressEvent>(std::bind(&FirstPersonHandler::OnKeyPressEvent, this, std::placeholders::_1));
        dispatcher.Dispatcher<KeyReleaseEvent>(std::bind(&FirstPersonHandler::OnKeyReleaseEvent, this, std::placeholders::_1));
    }
    
    void FirstPersonHandler::OnUpdate(int delta)
    {
        m_delta = (float)delta;
        m_camera.SetPos(glm::mix(m_camera.GetPos(), m_targetPos, 0.25f));
        m_camera.SetFront(MixSphere(m_camera.GetFront(), m_targetFront, 0.25f));

        if (static_cast<Widget&>(m_enHandle).IsHoveredWindow()) {
            if (Input::GetInstance().IsMousePressed(SEngine::MouseButton::SE_RIGHT)) {
                auto front = glm::normalize(m_camera.GetFront());
                auto up = glm::normalize(m_camera.GetUp());
                auto right = glm::normalize(glm::cross(front, up));
                float speed = m_moveSpeed * m_delta;
                if (Input::GetInstance().IsKeyPressed('a') || Input::GetInstance().IsKeyPressed('A')) {
                    m_targetPos = m_camera.GetPos() + (-right * speed);
                }
                if (Input::GetInstance().IsKeyPressed('d') || Input::GetInstance().IsKeyPressed('D')) {
                    m_targetPos = m_camera.GetPos() + (right * speed);
                }
                if (Input::GetInstance().IsKeyPressed('s') || Input::GetInstance().IsKeyPressed('S')) {
                    m_targetPos = m_camera.GetPos() + (-front * speed);
                }
                if (Input::GetInstance().IsKeyPressed('w') || Input::GetInstance().IsKeyPressed('W')) {
                    m_targetPos = m_camera.GetPos() + (front * speed);
                }
                if (Input::GetInstance().IsKeyPressed('q') || Input::GetInstance().IsKeyPressed('Q')) {
                    m_targetPos = m_camera.GetPos() + (-up * speed);
                }
                if (Input::GetInstance().IsKeyPressed('e') || Input::GetInstance().IsKeyPressed('E')) {
                    m_targetPos = m_camera.GetPos() + (up * speed);
                }
            }
        }
    }

    void FirstPersonHandler::MoveTarget(glm::vec3 target)
    {
        auto center = m_camera.GetPos() + m_camera.GetFront();
        m_targetPos = m_camera.GetPos() + target - center;
    }

    void FirstPersonHandler::OnMouseMoveEvent(MouseMoveEventPtr)
    {
        auto pos_ = Input::GetInstance().GetMousePos();
        glm::vec2 pos{pos_.first, pos_.second};
        if (static_cast<Widget&>(m_enHandle).IsHoveredWindow()) {
            if (Input::GetInstance().IsMousePressed(SEngine::MouseButton::SE_RIGHT)) {
                auto offset = pos - m_lastPos;
                if (offset.x < -50.0f || offset.x > 50.0f) { offset.x = 0.0f; }
                if (offset.y < -50.0f || offset.y > 50.0f) { offset.y = 0.0f; }
                // Log::GetInstance().Debug("{} {}", offset.x, offset.y);
                float rotateSpeed = m_rotateSpeed * m_delta;
                auto cameraTarget = CartesianToSpherical(m_camera.GetFront());
                cameraTarget.phi += glm::radians(-offset.x * rotateSpeed);
                cameraTarget.theta = glm::clamp(cameraTarget.theta + glm::radians(offset.y * rotateSpeed), glm::radians(10.0f), glm::radians(170.0f));
                m_targetFront = SphericalToCartesian(cameraTarget);
            }
        }
        m_lastPos = pos;
    }

    void FirstPersonHandler::OnMouseWheelEvent(MouseWheelEventPtr)
    {
        if (static_cast<Widget&>(m_enHandle).IsHoveredWindow()) {
            if (Input::GetInstance().IsMousePressed(SEngine::MouseButton::SE_RIGHT)) {
                auto alpha = Input::GetInstance().GetMouseWheelXY();
                m_moveSpeed += alpha.second * 0.01f;
                m_moveSpeed = glm::clamp(m_moveSpeed, 0.01f, 5.0f);
            } else {
                auto alpha = Input::GetInstance().GetMouseWheelXY();
                auto fov = m_camera.GetFov();
                fov = glm::clamp(fov + (float)alpha.second, 15.0f, 80.0f);
                m_camera.SetFov(fov);
            }
        }
    }

    void FirstPersonHandler::OnKeyPressEvent(KeyPressEventPtr e)
    {

    }
    
    void FirstPersonHandler::OnKeyReleaseEvent(KeyReleaseEventPtr e)
    {
    }
}